
package com.lee.tree; 
 
import com.lee.logger.Logger; 
import com.lee.utils.ConstantsUtils; 
import lombok.Data; 
import org.apache.commons.lang3.StringUtils; 
 
import java.util.*; 
 
@Data 
public class TiltedTimeNode { 
    transient private PatternTree tree;
    private String item;
    private boolean update;
    private List<Long> counts ;
    private List<Integer> cn;
    transient private TiltedTimeNode parent;
    transient private Map<String,TiltedTimeNode> children;
    public TiltedTimeNode(PatternTree tree, String item, int level){
        this.tree = tree;
        this.item = item;
        this.update = false;
        this.counts = new ArrayList<>();
        this.cn = new ArrayList<>();
        for(int i=0;i<level;i++){
            this.counts.add(0l);
            this.cn.add(0);
        }
 
        this.children = new HashMap<>();
    }
    public TiltedTimeNode(PatternTree tree, String item){
        this(tree,item, ConstantsUtils.TILTED_TIME_SIZE);
    }
    public void add(TiltedTimeNode child){
 
        if(!this.getChildren().containsKey(child.getItem())){
            this.getChildren().put(child.getItem(),child);
            child.setParent(this);
        }
    }
    public TiltedTimeNode search(String item){
        if(this.getChildren().containsKey(item)){
            return this.getChildren().get(item);
        }
        return null;
    }
    public boolean contains(String item){
        return this.getChildren().containsKey(item) ;
    }
 
 
 
    public boolean isRoot(){
        return StringUtils.isBlank(this.getItem()) && this.getCounts().size() == 0;
    }
 
    public boolean isLeaf(){
        return this.getChildren().size() == 0;
    }
 
    public List<TiltedTimeNode> allChildren(){
        List<TiltedTimeNode> children = new ArrayList<>(this.getChildren().values());
        children.sort(Comparator.comparing(TiltedTimeNode::getItem));
        return children;
    }
 
    public void inspect(){
        this.inspect(0);
    }
    public void inspect(int depth){
        Logger.info(String.join("", Collections.nCopies(depth*4," ")) + repr());
        int width = 0;
        for(TiltedTimeNode child:this.allChildren()){
            width += 1;
            if(depth < 5 && width < 5){
                child.inspect(depth + 1);
            }
 
        }
    }
    public void inspectAndUpdate(){
        this.inspectAndUpdate(0);
    }
    public void inspectAndUpdate(int depth){
        for(TiltedTimeNode child:this.allChildren()){
            child.inspectAndUpdate(depth + 1);
        }
        //对于未更新的结点，添加值0
        if(!this.update){
            this.setCounts(0);
        }
        this.setUpdate(false);
        //判断结点是否是叶子结点且counts全为0
        if(this.children.size() == 0 && new HashSet<Long>(this.getCounts()).size() == 1 && this.getCounts().contains(0)){
            //删除叶子结点
            this.getParent().getChildren().remove(this.getItem());
        }
    }
 
    public void inspectAndUpdateStack(){
        Stack<TiltedTimeNode> stack = new Stack<>();
        stack.push(this);
        do {
            TiltedTimeNode node = stack.pop();
            for(TiltedTimeNode child:node.allChildren()){
                stack.push(child);
            }
 
            //对于未更新的结点，添加值0
            if(!node.update){
                node.setCounts(0);
            }
            node.setUpdate(false);
            //判断结点是否是叶子结点且counts全为0
            if(node.children.size() == 0 && new HashSet<Long>(node.getCounts()).size() == 1 && node.getCounts().contains(0)){
                //删除叶子结点
                node.getParent().getChildren().remove(node.getItem());
            }
        }while(!stack.empty());
 
    }
 
 
 
 
 
    public String repr(){
        if (this.isRoot()){
            return String.format("<%s (root)>",this.getClass().getName());
        } else {
            return String.format("<%s %s (%s) (%s) %s>",this.getClass().getName(),this.getItem(),this.getCounts(),this.getCn(),this.isUpdate());
        }
    }
 
 
    @Override
    public String toString() {
 
        return String.format("TiltedTimeNode {%s(%s)}",this.item,this.getCounts().toString());
    }
 
    public PatternTree getTree() {
        return tree;
    }
 
    public void setTree(PatternTree tree) {
        this.tree = tree;
    }
 
    public String getItem() {
        return item;
    }
 
    public void setItem(String item) {
        this.item = item;
    }
 
    public boolean isUpdate() {
        return update;
    }
 
    public void setUpdate(boolean update) {
        this.update = update;
    }
 
    public List<Long> getCounts() {
        return counts;
    }
 
    public void setCounts(long value) {
        this.update = true;
        //倾斜时间窗口扫描添加数据
        List<Integer> pn = new ArrayList<>();
        for(int i=0;i<this.cn.size();i++){
            pn.add((int)Math.pow(2,i));
        }
        int tmpn = 1;
        long tmpcount = value;
        for(int i=0;i<pn.size();i++){
            if(tmpn != 0){
 
                if(this.cn.get(i) == 0){
                    //当前窗口为空
                    this.cn.set(i,tmpn);
                    this.counts.set(i,tmpcount);
                    break;
                } else if(this.cn.get(i) < pn.get(i)){
                    //当前窗口未满
                    this.cn.set(i,this.cn.get(i) + tmpn);
                    this.counts.set(i,this.counts.get(i) + tmpcount);
                    break;
                } else {
                    //当前窗口已满
                    int replacen = tmpn;
                    long replacecount = tmpcount;
                    tmpn = this.cn.get(i);
                    tmpcount = this.counts.get(i);
                    this.cn.set(i,replacen);
                    this.counts.set(i,replacecount);
                }
            }
        }
 
    }
 
 
 
    public TiltedTimeNode getParent() {
        return parent;
    }
 
    public void setParent(TiltedTimeNode parent) {
        this.parent = parent;
    }
 
    public Map<String, TiltedTimeNode> getChildren() {
        return children;
    }
 
    public void setChildren(Map<String, TiltedTimeNode> children) {
        this.children = children;
    }
} 
